﻿<template>
  <div>
    <el-upload
    :action = 'actionUrl' :auto-upload = "true" :http-request = "submitUpload"  :file-list = 'fileList'
    :accept = "(fileType === 'pic' || fileType === 'pics') ? 'image/*' : (fileType === 'video' ? '.mp4' : '')"
    :list-type = "(fileType === 'pic' || fileType === 'pics') ? 'picture-card' : 'text'"
    :limit = "limitCount ? limitCount : null" :class = "fileEditable ? '' : 'hiddenUploadInput'"
    :drag =  "(fileType === 'pic' || fileType === 'pics') ? false : true"
    :on-exceed = 'onExceed' :on-preview = "handleFilePreview" :before-remove = 'removeUpload' :multiple = 'shouldMultiple'>
      <template v-if = "(fileType === 'pic' || fileType === 'pics') && !showProgress">
        <i class = "el-icon-plus"></i>
      </template>
      <template v-if = "(fileType === 'file' || fileType === 'files' || fileType === 'video') && !showProgress">
        <i class = "el-icon-upload"></i>
        <div class = "el-upload__text">将文件拖到此处，或<em>点击上传</em></div>
      </template>
      <template v-if = 'showProgress'>
        <el-progress type = "circle" :percentage = "currentPercentage"
        :width = '120' style = 'margin-top:10px' :format = 'progressFormat'></el-progress>
      </template>
    </el-upload>
    <!-- 预览图片的弹窗 -->
    <el-dialog :visible.sync = "dialogVisible" append-to-body title = '图片预览' width = '50%'>
      <el-image style = 'width:100%;height:100%'  class = "show_uploadPic"
      :src = "dialogImageUrl" title = '点击查看原图' :preview-src-list = 'previewSrcList'></el-image>
    </el-dialog>
    <!-- 没有图片或者文件时展示的提示信息--不可编辑状态时 -->
    <span v-if = "(fileType === 'pic' || fileType === 'file') && !fileEditable && !uploadFileData">{{fileType === 'pic' ? '暂无图片' : '暂无文件'}}</span>
    <span v-if = "(fileType === 'pics' || fileType === 'files') && !fileEditable && (uploadFileData && !uploadFileData.length)">{{fileType === 'pics' ? '暂无图片' : '暂无文件'}}</span>
  </div>
</template>

<script>
import axios from 'axios'
export default {
  // 组件需要接受的参数有：
  // fileType: 上传的文件类型，file--单个文件，files-多个文件(默认值，表示对上传的文件类型和数量均没有限制)，pic--单个图片，pics--多个图片,vodeo--视频类型
  // fileData: 已上传的文件的路径，一般多个文件时，是数组结构，单个文件时是字符串结构
  // fileLimit: 上传文件的数量限制，数据模式，为空时表示不限制
  // fileInit: 表示是否需要刷新当前组件（该属性值为Boolean类型，一般是父组件需要刷新当前组件时，会改变该值）
  // fileEditable: 表示当前组件的编辑状况--true-可编辑
  props: {
    fileType: {
      type: String,
      default: 'files'
    },
    fileData: {
      type: [String, Array]
    },
    fileLimit: {
      type: Number,
      default: null
    },
    fileInit: {
      type: Boolean,
      default: false
    },
    fileEditable: {
      type: Boolean,
      default: true
    }
  },
  data () {
    return {
      // 上传的服务地址--可忽略
      actionUrl: `${$URL.IMG_UPLOAD_URL}`,
      // el-upload展示文件的数组
      fileList: [],
      limitCount: null,
      fileReader: '',
      // 上传后文件地址的存储变量
      uploadFileData: null,
      // 图片预览部分
      dialogImageUrl: '',
      dialogVisible: false,
      previewSrcList: [],
      // 上传进度展示
      showProgress: false,
      currentPercentage: 0,
      shouldMultiple: false, // 是否支持多选文件
      choosePicArray: [], // 所选择的图片的file的集合
      uploadingIndex: 0 // 当前上传的图片的位置
    }
  },
  mounted () {
    if (!window.FileReader) {
      this.$message({
        message: '您的浏览器不支持 FileReader API',
        type: 'warning'
      })
    } else {
      this.fileReader = new FileReader()
    }
    this.initComponent()
  },
  methods: {
    // 初始化该组件
    initComponent () {
      // 先清空展示图片或者文件的列表
      this.fileList = []
      this.previewSrcList = []
      // 控制可多选图片的变量
      this.shouldMultiple = false
      this.choosePicArray = []
      this.uploadingIndex = 0
      // 判断该上传文件的类型
      // 文件类型的，需要将上传的文件展示在控件下方
      // 图片类型的，需要获取图片的base64，并将图片展示在控件中
      switch (this.fileType) {
        case 'file': {
          this.uploadFileData = this.fileData ? this.fileData : ''
          this.limitCount = 1
          // 如果有已上传的文件（父组件传递过来的），则将其展示在控件的下方
          if (this.uploadFileData) {
            let obj = {
              name: this.uploadFileData,
              url: this.uploadFileData
            }
            if (this.uploadFileData.indexOf('/name/') !== -1) {
              obj.name = this.uploadFileData.slice(this.uploadFileData.indexOf('/name/') + 6)
            }
            this.fileList.push(obj)
          }
          break
        }
        case 'video': {
          this.uploadFileData = this.fileData ? this.fileData : ''
          this.limitCount = 1
          // 如果有已上传的文件（父组件传递过来的），则将其展示在控件的下方
          if (this.uploadFileData) {
            let obj = {
              name: this.uploadFileData,
              url: this.uploadFileData
            }
            if (this.uploadFileData.indexOf('/name/') !== -1) {
              obj.name = this.uploadFileData.slice(this.uploadFileData.indexOf('/name/') + 6)
            }
            this.fileList.push(obj)
          }
          break
        }
        case 'files': {
          this.uploadFileData = this.fileData ? JSON.parse(JSON.stringify(this.fileData)) : []
          this.limitCount = this.fileLimit ? this.fileLimit : null
          // 如果有已上传的文件（父组件传递过来的），则将其展示在控件的下方
          if (this.uploadFileData && this.uploadFileData.length) {
            this.uploadFileData.forEach(item => {
              let obj = {
                name: item,
                url: item
              }
              if (item.indexOf('/name/') !== -1) {
                obj.name = item.slice(item.indexOf('/name/') + 6)
              }
              this.fileList.push(obj)
            })
          }
          break
        }
        case 'pic': {
          this.uploadFileData = this.fileData ? this.fileData : ''
          this.limitCount = 1
          if (this.uploadFileData) {
            let obj = {
              linkUrl: this.uploadFileData,
              url: this.uploadFileData.indexOf('/dl/') !== -1 ? this.uploadFileData.replace('/dl/', '/') : this.uploadFileData
            }
            this.fileList.push(obj)
          }
          break
        }
        case 'pics': {
          this.shouldMultiple = true
          this.limitCount = this.fileLimit ? this.fileLimit : null
          this.uploadFileData = this.fileData ? JSON.parse(JSON.stringify(this.fileData)) : []
          if (this.uploadFileData && this.uploadFileData.length) {
            this.uploadFileData.forEach((item) => {
              let obj = {
                linkUrl: item,
                url: (item && item.indexOf('/dl/') !== -1) ? item.replace('/dl/', '/') : item
              }
              this.fileList.push(obj)
            })
          }
          break
        }
      }
    },
    // 上传文件、图片的方法
    submitUpload (options) {
      let file = options.file
      if (this.shouldMultiple) { // 可以选择多个上传时
        if (file) {
          this.choosePicArray.push(file)
        }
        if (this.choosePicArray && this.choosePicArray.length && !this.uploadingIndex) {
          // 选择有图片,并且还没有执行上传的方法时
          this.uploadingIndex = 1 // 开始执行上传文件的方法
          this.showProgress = true
          this.currentPercentage = 0
          this.submitUploadMultipleMethod(this.choosePicArray[0])
        }
      } else {
        this.submitUploadMethod(file)
      }
    },
    // 执行上传多个文件时的方法
    submitUploadMultipleMethod (file) {
      let filename = file.name
      if (file) {
        this.fileReader.readAsDataURL(file)
        let formData = new FormData()
        formData.append('file', file)
        axios({
          url: $URL.IMG_UPLOAD_URL + '/fileset/upload_stream',
          method: 'post',
          headers: {
            'Content-Type': 'multipart/form-data;boundary = ' + new Date().getTime()
          },
          data: formData
        }).then(data => {
          let dataResult = data.data.result
          // 上传成功时
          if (dataResult.code === 'success') {
            if (this.fileType === 'file' || this.fileType === 'files') {
              let obj = {
                name: filename,
                url: dataResult.link_url + '/name/' + filename
              }
              this.fileList.push(obj)
              // 单个文件为字符串，多个文件为数组
              if (this.fileType === 'file') {
                this.uploadFileData = dataResult.link_url + '/name/' + filename
              } else if (this.fileType === 'files') {
                this.uploadFileData.push(dataResult.link_url + '/name/' + filename)
              }
            } else if (this.fileType === 'pic' || this.fileType === 'pics') {
              let obj = {
                linkUrl: dataResult.link_url,
                url: (dataResult.link_url && dataResult.link_url.indexOf('/dl/') !== -1) ? dataResult.link_url.replace('/dl/', '/') : dataResult.link_url
              }
              this.fileList.push(obj)
              // 单个文件为字符串，多个文件为数组
              if (this.fileType === 'pic') {
                this.uploadFileData = dataResult.link_url
              } else if (this.fileType === 'pics') {
                this.uploadFileData.push(dataResult.link_url)
              }
            } else if (this.fileType === 'video') {
              let obj = {
                name: filename,
                url: dataResult.link_url + '/name/' + filename
              }
              this.fileList.push(obj)
              this.uploadFileData = dataResult.link_url + '/name/' + filename
            }
            // 更新父组件中的值
            this.$emit('update:fileData', this.uploadFileData)
            if (this.uploadingIndex >= this.choosePicArray.length) { // 当图片都上传完成时
              this.currentPercentage = 100
              this.$message({
                message: '上传成功',
                type: 'success'
              })
              this.showProgress = false
              this.choosePicArray = []
              this.uploadingIndex = 0
            } else {
              this.uploadingIndex = this.uploadingIndex + 1
              let currentPer = parseInt(this.uploadingIndex / (this.choosePicArray.length + 1) * 10000) / 100
              this.currentPercentage = currentPer > 100 ? 100 : currentPer
              this.submitUploadMultipleMethod(this.choosePicArray[this.uploadingIndex - 1])
            }
          } else {
            // 上传失败的提示
            this.uploadFailedMsg()
          }
        }).catch(error => {
          // 上传失败的提示
          this.uploadFailedMsg()
          console.log(error)
        })
      }
    },
    // 执行上传的方法
    submitUploadMethod (file) {
      let filename = file.name
      if (file) {
        // 进度条相关的控件
        this.showProgress = true
        this.currentPercentage = 0
        this.fileReader.readAsDataURL(file)
        let formData = new FormData()
        formData.append('file', file)
        axios({
          url: $URL.IMG_UPLOAD_URL + '/fileset/upload_stream',
          method: 'post',
          headers: {
            'Content-Type': 'multipart/form-data;boundary = ' + new Date().getTime()
          },
          data: formData,
          onUploadProgress: ProgressEvent => {
            if (ProgressEvent.lengthComputable) {
              let currentPer = parseInt(ProgressEvent.loaded / ProgressEvent.total * 10000) / 100
              this.currentPercentage = currentPer > 100 ? 100 : currentPer
            }
          }
        }).then(data => {
          let dataResult = data.data.result
          // 上传成功时
          if (dataResult.code === 'success') {
            this.currentPercentage = 100
            if (this.fileType === 'file' || this.fileType === 'files') {
              let obj = {
                name: filename,
                url: dataResult.link_url + '/name/' + filename
              }
              this.fileList.push(obj)
              // 单个文件为字符串，多个文件为数组
              if (this.fileType === 'file') {
                this.uploadFileData = dataResult.link_url + '/name/' + filename
              } else if (this.fileType === 'files') {
                this.uploadFileData.push(dataResult.link_url + '/name/' + filename)
              }
            } else if (this.fileType === 'pic' || this.fileType === 'pics') {
              let obj = {
                linkUrl: dataResult.link_url,
                url: (dataResult.link_url && dataResult.link_url.indexOf('/dl/') !== -1) ? dataResult.link_url.replace('/dl/', '/') : dataResult.link_url
              }
              this.fileList.push(obj)
              // 单个文件为字符串，多个文件为数组
              if (this.fileType === 'pic') {
                this.uploadFileData = dataResult.link_url
              } else if (this.fileType === 'pics') {
                this.uploadFileData.push(dataResult.link_url)
              }
            } else if (this.fileType === 'video') {
              let obj = {
                name: filename,
                url: dataResult.link_url + '/name/' + filename
              }
              this.fileList.push(obj)
              this.uploadFileData = dataResult.link_url + '/name/' + filename
            }
            this.$message({
              message: '上传成功',
              type: 'success'
            })
            this.showProgress = false
            // 更新父组件中的值
            this.$emit('update:fileData', this.uploadFileData)
          } else {
            // 上传失败的提示
            this.uploadFailedMsg()
          }
        }).catch(error => {
          // 上传失败的提示
          this.uploadFailedMsg()
          console.log(error)
        })
      }
    },
    // 进度条的文字格式化
    progressFormat (percentage) {
      return percentage === 100 ? '处理中...' : `${percentage}%`
    },
    // 上传失败的提示
    uploadFailedMsg () {
      // 上传失败时，先清空上传列表中的内容
      this.fileList = []
      this.$refs.upload.clearFiles()
      // 将以成功上传的文件展示在上传列表中--不是单个文件时
      if (this.uploadFileData && this.uploadFileData.length) {
        this.uploadFileData.forEach(item => {
          if (item) {
            if (this.fileType === 'files') {
              let obj = {
                name: item.indexOf('/name/') !== -1 ? item.slice(item.indexOf('/name/') + 6) : item,
                url: item
              }
              this.fileList.push(obj)
            } else if (this.filetype === 'pics') {
              let obj = {
                url: item.indexOf('/dl/') !== -1 ? item.replace('/dl/', '/') : item,
                linkUrl: item
              }
              this.fileList.push(obj)
            }
          }
        })
      }
      this.$message({
        message: '上传失败',
        type: 'error',
        duration: 3000
      })
      this.showProgress = false
    },
    // 上传文件超过最大上传个数时
    onExceed () {
      let msg = (this.fileType === 'pic' || this.fileType === 'pics') ? ('最多只能上传' + this.limitCount + '个图片') : ('最多只能上传' + this.limitCount + '个文件')
      this.$message({
        message: msg,
        type: 'warning'
      })
    },
    // 上传文件、图片移除
    removeUpload (file) {
      if (this.fileEditable) {
        let msg = (this.fileType === 'pics' || this.fileType === 'pic') ? '确定要移除该图片么,请谨慎操作！' : '确定要移除该文件么,请谨慎操作！'
        this.$confirm(msg, '提示', {
          showClose: false,
          showCancelButton: true,
          confirmButtonText: '确定',
          cancelButtonClass: 'el-button--info',
          cancelButtonText: '取消'
        }).then(() => {
          // 删除文件的方法
          let downUrl = (this.fileType === 'pics' || this.fileType === 'pic') ? file.linkUrl : file.url
          if (!downUrl) {
            return
          }
          if (downUrl.indexOf('/name/') !== -1) {
            downUrl = downUrl.slice(0, downUrl.indexOf('/name/'))
          }
          if (downUrl) {
            if (downUrl.indexOf('/fileset/dl/') !== -1) {
              downUrl = downUrl.replace('/fileset/dl/', '/fileset/recycle/')
            } else {
              downUrl = downUrl.replace('/fileset/', '/fileset/recycle/')
            }
          }
          this.$store.dispatch('changeLoadingText', '操作中...')
          this.$store.dispatch('changRouteLoading', true)
          axios({
            url: downUrl,
            method: 'patch'
          }).then(data => {
            let dataResult = data.data.result
            if (dataResult.code === 'success') {
              // 当个图片、文件时
              if (this.fileType === 'pic' || this.fileType === 'file' || this.fileType === 'video') {
                this.uploadFileData = ''
                this.fileList = []
              } else if (this.fileType === 'pics') {
                // 多个图片时
                if (this.uploadFileData && this.uploadFileData.length) {
                  this.uploadFileData.forEach((item, index) => {
                    if (item === file.linkUrl) {
                      this.uploadFileData.splice(index, 1)
                    }
                  })
                }
                // 移除展示的图片
                if (this.fileList.length) {
                  this.fileList.forEach((item, index) => {
                    if (item.url === file.url) {
                      this.fileList.splice(index, 1)
                    }
                  })
                }
              } else if (this.fileType === 'files') {
                // 多个文件时
                if (this.uploadFileData && this.uploadFileData.length) {
                  this.uploadFileData.forEach((item, index) => {
                    if (item === file.url) {
                      this.uploadFileData.splice(index, 1)
                    }
                  })
                }
                // 移除展示的文件
                if (this.fileList.length) {
                  this.fileList.forEach((item, index) => {
                    if (item.url === file.url) {
                      this.fileList.splice(index, 1)
                    }
                  })
                }
              }
              this.$message({
                message: '删除成功',
                type: 'success'
              })
              // 更新父组件中的值
              this.$emit('update:fileData', this.uploadFileData)
            } else {
              this.$message({
                message: dataResult.msg,
                type: 'error',
                duration: 5000
              })
            }
            this.$store.dispatch('changeLoadingText', '数据加载中...')
            this.$store.dispatch('changRouteLoading', false)
          }).catch(error => {
            console.log(error)
            this.$store.dispatch('changeLoadingText', '数据加载中...')
            this.$store.dispatch('changRouteLoading', false)
          })
        }).catch(() => {
        })
        return false
      } else {
        return false
      }
    },
    // 点击图片预览，点击文件名称时，下载文件
    handleFilePreview (file) {
      if (this.fileType === 'pic' || this.fileType === 'pics') {
        // 图片类型时，预览图片
        this.previewSrcList = [file.url]
        this.dialogImageUrl = file.url
        this.dialogVisible = true
      } else {
        // 文件类型时，点击下载文件
        this.$confirm('确定要下载该文件么', '提示', {
          showClose: false,
          showCancelButton: true,
          confirmButtonText: '确定',
          cancelButtonClass: 'el-button--info',
          cancelButtonText: '取消'
        }).then(() => {
          this.getFileBasicInfo(file.url, 'download', null)
        }).catch(() => {})
      }
    },
    // 获取文件、图片的详细信息（参数为：图片或者文件的url；获取的类型--下载和获取图片的base64）
    getFileBasicInfo (fileUrl, getInfoType) {
      if (!fileUrl) {
        this.$message({
          message: '没有获取到改文件的地址信息',
          type: 'error'
        })
        return
      }
      let downloadUrl = fileUrl.indexOf('/name/') !== -1 ? fileUrl.slice(0, fileUrl.indexOf('/name/')) : fileUrl
      // if (downloadUrl.indexOf('/fileset/dl/') !== -1) {
      //   downloadUrl = downloadUrl.replace('/fileset/dl/', '/fileset/')
      // }
      axios({
        url: downloadUrl,
        method: 'get'
      }).then(data => {
        let dataInfo = data.data.result
        if (dataInfo.code === 'success') {
          // 当为下载时
          if (getInfoType === 'download') {
            let content = decodeURIComponent(dataInfo.file)
            let blobContent = this.toBlob(content, dataInfo.filetype)
            this.downloadblob(blobContent, dataInfo.filename)
          }
        }
      })
    },
    // 下载文件到本地
    downloadblob (blobcontent, filename) {
      if ('msSaveOrOpenBlob' in navigator) {
        // Microsoft Edge and Microsoft Internet Explorer 10-11
        window.navigator.msSaveOrOpenBlob(blobcontent, filename)
      } else {
        // 创建隐藏的可下载链接
        var eleLink = document.createElement('a')
        eleLink.download = filename
        eleLink.style.display = 'none'
        // 字符内容转变成blob地址
        eleLink.href = URL.createObjectURL(blobcontent)
        // 触发点击
        document.body.appendChild(eleLink)
        eleLink.click()
        // 然后移除
        document.body.removeChild(eleLink)
      }
    },
    // 将base64装为文件
    toBlob (base64str, filetype) {
      let bytes = window.atob(base64str)
      let n = bytes.length
      let u8arr = new Uint8Array(n)
      while (n--) {
        u8arr[n] = bytes.charCodeAt(n)
      }
      return new Blob([u8arr], {type: filetype})
    }
  },
  watch: {
    fileInit: {
      handler (val) {
        if (val) {
          this.initComponent()
        }
      },
      deep: true
    }
  }
}
</script>
